﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Threading;
using System.Threading.Tasks;

namespace FrogEngine
{
	/*
	 * Order of Update:
	 *		- Service Modification Logic / Game State Logic
	 *		- Input Services
	 *		- Game Logic Services
	 *		- A.I. Services
	 *		- Physics Services
	 *		- Animation Services
	 *		- Particles/Effects Services
	*/

	/// <summary>
	/// Just derive an engine class from this base and override Initialize and Cleanup to create 
	/// all your GameEntityServices and add them to the EntityServices collection.
	/// </summary>
	public abstract class Engine : IEngine
	{
		public Engine(Game game, GameWindow window, GraphicsDevice graphicsDevice, SpriteBatch spriteBatch, ContentManager contentManager)
		{
			this.game = game;
			this.window = window;
			this.graphicsDevice = graphicsDevice;
			this.spriteBatch = spriteBatch;
			this.contentManager = contentManager;
			entities = new HashSet<IGameEntity>();
			entityServices = new HashSet<IGameEntityService>();
		}

		public T GetService<T>() where T : IGameEntityService
		{
			return (T)EntityServices.First(a => a is T);
		}

		public IEnumerable<T> GetServices<T>() where T : IGameEntityService
		{
			return EntityServices.Where(a => a is T).Cast<T>();
		}

		protected Game game;
		public Game Game
		{
			get { return game; }
		}

		protected GameWindow window;
		public GameWindow Window
		{
			get { return window; }
		}

		protected GraphicsDevice graphicsDevice;
		public GraphicsDevice GraphicsDevice
		{
			get { return graphicsDevice; }
		}

		protected SpriteBatch spriteBatch;
		public SpriteBatch SpriteBatch
		{
			get { return spriteBatch; }
		}

		protected ContentManager contentManager;
		public ContentManager ContentManager
		{
			get { return contentManager; }
		}

		protected HashSet<IGameEntity> entities;
		public ISet<IGameEntity> Entities
		{
			get { return entities; }
		}

		protected HashSet<IGameEntityService> entityServices;
		public ISet<IGameEntityService> EntityServices
		{
			get { return entityServices; }
		}

		public void Update(GameTime gameTime)
		{
			var updateServices = GetServices<IUpdateEntityService>().OrderBy(a => a.ServiceOrder);
			foreach (var service in updateServices)
			{
				service.Update(gameTime);
			}
		}

		public void Render(GameTime gameTime)
		{
			var renderServices = GetServices<IRenderEntityService>();
			foreach (var service in renderServices)
			{
				service.Render(gameTime);
			}
		}

		public void Exit()
		{
			this.Game.Exit();
		}

		public abstract void Initialize();

		public abstract void Cleanup();
	}
}
